#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>

#include "w3ld.h"
#include "perror_exit.h"
#include "func_lexer.h"
#include "server_protocol.h"

extern global_frame frame;

cmd_ret *
parse( char *line )
{

	char *s,*t;
	w3l_cmd_entry *cmd = NULL;
	char **args = NULL;
	int arity;

	assert(line != NULL);

	if(!(s = strchr(line,'(')))
		return W3L_FUNC_ERR(W3L_ERR_ILL_FORMED);
	*s = '\0';
	++s;

	if(!(t = strrchr(s,')')))
		return W3L_FUNC_ERR(W3L_ERR_ILL_FORMED);
	*t = '\0';

	if(!(cmd = lexer_fetch(line)))
		return W3L_FUNC_ERR(W3L_ERR_FUNC_NOT_FOUND);

	arg_parse(s,&args,&arity);

	return (cmd->handler)(arity,args);
}

cmd_ret *
construct_return
(
	void *data, 
	char data_type, 
	long data_length, 
	char free_data
)
{
	cmd_ret *w3l_return = NULL;	
	MALLOCATE(w3l_return,cmd_ret *,sizeof(cmd_ret))

	w3l_return->data = data;
	w3l_return->data_type = data_type;
	w3l_return->data_length = data_length;
	w3l_return->free_data = free_data;
	
	return w3l_return;
}

void destroy_return(cmd_ret *cmd) {
	
	assert(cmd != NULL);
	
	if(cmd->free_data) {
		assert(cmd->data != NULL);
		free(cmd->data);
	}
	
	free(cmd);
}

func_tree_node *init_func_tree_node() {
	int i;
	func_tree_node *tn_tmp = NULL;
	MALLOCATE(tn_tmp,func_tree_node *,sizeof(func_tree_node))
	tn_tmp->cmd_entry = NULL;
	for(i=0;i<FUNCTION_TREE_HASH_SIZE;i++)
		(tn_tmp->children)[i] = NULL;	
	return tn_tmp;
}

void
lexer_insert ( w3l_cmd_entry *cmd )
{
	char *s = NULL;
	func_tree_node *ftn = NULL;
	
	assert(cmd != NULL);
	assert(cmd->fname != NULL);
	assert(frame.froot != NULL);

	s = (char *)&(cmd->fname[0]);
	ftn = frame.froot;
	
	while((*s)!='\0') {
		if(!((ftn->children)[(int)(*s)]))
			ftn->children[(int)(*s)] = init_func_tree_node();
		ftn = ftn->children[(int)(*s)];
		++s;
	}

	ftn->cmd_entry = cmd;
}

w3l_cmd_entry *
lexer_fetch ( char *fname )
{
	char *s = NULL;
	func_tree_node *ftn = NULL;

	assert(fname != NULL);
	assert(frame.froot != NULL);

	s = &(fname[0]);
	ftn = frame.froot;

	while((*s)!='\0') {
		if(!(ftn->children[(int)(*s)]))
			return NULL;
		ftn = ftn->children[(int)(*s)];
		++s;
	}

	if(!ftn->cmd_entry)
		return NULL;

	return ftn->cmd_entry;
}

LNode_type *list_functions_(func_tree_node *ftn) {

	LNode_type *ret, *ptr;	
	int i;

	assert(ftn != NULL);

	ret = init_linked_list();

	for(i=0;i<FUNCTION_TREE_HASH_SIZE;i++) {
		if(ftn->children[i]) {
			
			if(((w3l_cmd_entry *)(ftn->children[i]))->fname != NULL)
				Insert_linked_list(ret,create_new_node_llist((char *)((w3l_cmd_entry *)(ftn->children[i]))->fname));

			ptr = list_functions_(ftn->children[i]);
			assert(ptr != NULL);

			//void llist_concat(LNode_type *ret, LNode_type *ptr)
			{
				LNode_type *tmp;

				assert(ret != NULL);
				assert(ptr != NULL);

				tmp = ret->back;
				ret->back = ptr->back;
				ptr->back->next = ret;

				tmp->next = ptr->next;
				ptr->next->back = tmp;

				free(ptr);
				tmp = NULL;
			}
		}
	}

	assert(ret != NULL);
	return ret;	
}

LNode_type *list_functions() { return list_functions_(frame.froot); }

void arg_parse(char *arg_string, char ***args, int *arity)
{ /* this is a dangerous little function */
	char *s = NULL;
	*arity = 0;
	
	assert(arg_string != NULL);
	
	while((s = strchr(arg_string,','))) {
		*s = '\0';
		REMALLOCATE(*args,char **,sizeof(char *)*((*arity)+1))
		(*args)[(*arity)++]=strdup(arg_string);
		arg_string = ++s;
	}
	if(*arg_string != '\0') {
		REMALLOCATE(*args,char **,sizeof(char *)*(*arity+1))
		(*args)[(*arity)++]=strdup(arg_string);
	}
}

void
w3l_load_command
(
	char *fname,
	char *fdesc,
	W3L_FUNC_RETURN(*handler)(W3L_PARAMETERS),
	int arity,
	...
)
{
	int i;
	va_list ap;
	w3l_cmd_entry *cmd_tmp = NULL;

	if(!(cmd_tmp = (w3l_cmd_entry *)malloc(sizeof(w3l_cmd_entry))))
		perror_exit("%s %d",__FILE__,__LINE__);

	cmd_tmp->fname = strdup(fname);
	cmd_tmp->handler = handler;
	cmd_tmp->fdesc = strdup(fdesc);
	cmd_tmp->arity = arity;
	if(arity > 0)
		MALLOCATE(cmd_tmp->arguments,char *,sizeof(char *)*arity)

	va_start(ap,arity);
	for(i=0;i<arity;i++)
		cmd_tmp->arguments[i] = strdup(va_arg(ap,char *));
	va_end(ap);

	lexer_insert(cmd_tmp);
}
